package com.kevin.v1.springframework.servlet;

import com.kevin.v1.springframework.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class KvServletBak extends HttpServlet {

   private Properties properties = new Properties();

   //存储扫描的包下的类名 及包名
   private Map<String, String> beanName = new HashMap<String, String>();

   //存放spring 实例化的bean
   private Map<String, Object> ioc = new HashMap<String, Object>();


   private Map<String, Method> handdlerMapping = new HashMap<String, Method>();


   private List<Handler> handermapping = new ArrayList<Handler>();


   @Override
   protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     doPost(req, resp);
   }

   @Override
   protected void doPost (HttpServletRequest req, HttpServletResponse resp) {
      try {
         doDisPath(req, resp);
      } catch (Exception e) {
         e.printStackTrace();
      }
   }


   private void doDisPath (HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
      String url = req.getRequestURL().toString();
      String contextPath = req.getContextPath();


      String path= url.substring(url.indexOf(contextPath), url.length());
      path = path.replaceAll(contextPath, "").replaceAll("/+", "/");

      Handler hander = getHander(path);
      if ( hander== null) {
         try {
            resp.getWriter().write("404 not found!");

         } catch (IOException e) {
            e.printStackTrace();
         }
         return;
      }

      Method method = hander.getMethod();
      method.invoke(hander.getController(), new Object[]{"SB"});

   }

   private Handler getHander (String url) {
      for (Handler handler : handermapping) {
         if (handler.getUrl().equals(url))
            return handler;
      }

      return null;
   }

   @Override
   public void init (ServletConfig config) {
      System.out.println("init KvServletBak ");
      try {
         //加载spring配置文件
         loadServletConfig();

         //扫描出spring 管理的所有bean
         scanPackage();

         //初始化IOC 容器
         initIoc();

         //DI 依赖注入
         dependencyInjection();


         //注册handderMapping
         registerHannerMapping();


      } catch (Exception e) {

         e.printStackTrace();
         System.out.println("初始化" + this.getClass().getSimpleName() + "异常:" + e);
      }


   }


   private void initIoc () {
      if (beanName.isEmpty()) return;
      for (Map.Entry<String, String> beans : beanName.entrySet()) {
         try {
            String beanName = null;
            Class<?> clazz = Class.forName(beans.getKey());
            if (clazz.isAnnotationPresent(KvController.class)) {
               KvController kvController = clazz.getAnnotation(KvController.class);
               beanName = kvController.value();
            } else if (clazz.isAnnotationPresent(KvService.class)) {
               KvService kvService = clazz.getAnnotation(KvService.class);
               beanName = kvService.value();
            }
            if ("".equals(beanName.trim())) {
               beanName = BeanNametoLowerCase(clazz.getSimpleName());
            }

            ioc.put(beanName, clazz.newInstance());

         } catch (ClassNotFoundException e) {
            e.printStackTrace();
         } catch (IllegalAccessException e) {
            e.printStackTrace();
         } catch (InstantiationException e) {
            e.printStackTrace();
         }

      }


   }


   private void dependencyInjection () throws IllegalAccessException {
      if (ioc.isEmpty()) return;

      for (Map.Entry<String, Object> bean : ioc.entrySet()) {
         Class<?> clazz = bean.getValue().getClass();
         //拿到此类的所有字段
         Field[] declaredFields = clazz.getDeclaredFields();
         //循环判断是否有@KvAutowired修饰的对象
         for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(KvAutowired.class)) {
//               KvAutowired autowired = declaredField.getAnnotation(KvAutowired.class);
               //拿到类名 首字母转小写
               String beanName = BeanNametoLowerCase(declaredField.getType().getName());
               //强制赋值 强吻
               declaredField.setAccessible(true);

               //从ioc 取出实例化好的 对象 注入到autowired修饰的 字段上
               declaredField.set(bean.getValue(), ioc.get(beanName));

            }
         }

      }


   }


   private void registerHannerMapping () {
      if (ioc.isEmpty()) return;

      for (Map.Entry<String, Object> beans : ioc.entrySet()) {
         Class<?> clazz = beans.getValue().getClass();
         if (!clazz.isAnnotationPresent(KvController.class)) continue;
         KvRequestMapping requestMapping = clazz.getAnnotation(KvRequestMapping.class);
         String baseUrl = "/" + requestMapping.value();

         Method[] methods = clazz.getMethods();
         for (Method method : methods) {
            if (!method.isAnnotationPresent(KvRequestMapping.class)) continue;
            KvRequestMapping methodrequestMapping = method.getAnnotation(KvRequestMapping.class);

            baseUrl = (baseUrl + "/" + methodrequestMapping.value()).replaceAll("/+", "/");


            //获取方法上所有形参类型
//            Class<?>[] parameterTypes = method.getParameterTypes();
//            Map<String, Integer> paramMap = new HashMap<String, Integer>();
//            for (int i = 0; i < parameterTypes.length; i++) {
//               paramMap.put(parameterTypes.getClass().getSimpleName(), i);
//            }

            Handler handler = new Handler(baseUrl, method, beans.getValue());
            handermapping.add(handler);

//            handdlerMapping.put(baseUrl, method);
            System.out.println("Mapped :" + baseUrl + "," + method);
         }


      }


   }


   /**
    * 扫描 配置文件 scanPackage 下的包 将所有beanPath路径保存起来
    */
   private void scanPackage () {

      doScanner(properties.getProperty("scanPackage"));


   }

   /**
    * 扫描 配置文件 scanPackage 下的包 将所有beanPath路径保存起来
    *
    * @param scanPackage
    */
   private void doScanner (String scanPackage) {
      String url = this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\.", "/")).getFile();
      File file = new File(url);
      for (File file1 : file.listFiles()) {
         if (file1.isDirectory()) {
            url = scanPackage + "." + file1.getName();
            doScanner(url);
         } else {
            if (!file1.getName().endsWith("class")) continue;
            String beanPath = scanPackage + "." + file1.getName().substring(0,file1.getName().lastIndexOf("."));
            beanName.put(beanPath, null);

         }
      }
   }


   private void loadServletConfig () throws IOException {

      //通过类的 类加载器
      InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("application.properties");
      properties.load(resourceAsStream);

   }


   /**
    * 将beanName
    * 首字符小写作为ioc容器里的 key
    *
    * @param simpleName
    * @return
    */
   String BeanNametoLowerCase (String simpleName) {
      char[] car = simpleName.toCharArray();
      //首字符+32转小写
      //首字母-32转大写
      car[0] += 32;
      return String.valueOf(car);
   }


   private class Handler {
      private String url;
      private Method method;
      private Object controller;
      private Map<String, Integer> paramindexMap;

      private Class<?>[] paramTypes;

      public Handler (String url, Method method, Object controller) {
         this.url = url;
         this.method = method;
         this.controller = controller;

         this.paramTypes = method.getParameterTypes();
         paramindexMap = new HashMap<String, Integer>();
         putparamIndexMap();
      }

      /**
       * put 注解下的参数名及位置
       */
      private void putparamIndexMap () {
         //二维数组一个方法有多个参数 一个参数有多个注解
         Annotation[][] parameterAnnotations = method.getParameterAnnotations();
         for (int i = 0; i < parameterAnnotations.length; i++) {
            for (Annotation annotation : parameterAnnotations[i]) {
               if (annotation instanceof KvRequestParam) {
                  String paraName = ((KvRequestParam) annotation).value();
                  if (!paraName.trim().equals("")) {
                     paramindexMap.put(paraName, i);
                  }
               }
            }
         }

         /**
          * put 判断方法是否有HttpServletRequest 和HttpServletResponse 作为形参
          */
         for (int i = 0; i < paramTypes.length; i++) {
            Class<?> clazz = paramTypes[i];
            if (clazz == HttpServletRequest.class) {
               paramindexMap.put(clazz.getSimpleName(), i);
            } else if (clazz == HttpServletResponse.class) {
               paramindexMap.put(clazz.getSimpleName(), i);
            }

         }


      }


      public String getUrl () {
         return url;
      }

      public Method getMethod () {
         return method;
      }

      public Object getController () {
         return controller;
      }

      public Map<String, Integer> getParamindexMap () {
         return paramindexMap;
      }

      public Class<?>[] getParamTypes () {
         return paramTypes;
      }
   }


}
